/* 
 *  <copyright file="Parameters.cs" company="Wesley Jowitt">
 *  
 *  SobrietyEngine: Copyright (c) 2011 Wesley Jowitt
 *  
 *  This software is distributed under a software license.
 *   * As of the creation time of this file, the license in use is the Microsoft Permissive License (Ms-PL) 
 *    * The license can be viewed at:
 *     * License.Txt in the root folder of this project
 *      * http://sobrietyengine.codeplex.com/license
 *       * http://www.microsoft.com/opensource/licenses.mspx
 *       
 *  </copyright>
 *   */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace SobrietyEngine.Common
{
        //[ContentTypeReader]
        public class ParametersContentReader : ContentTypeReader<Parameters>
        {
            protected override Parameters Read(
            ContentReader input,
            Parameters existingInstance)
            {
                Parameters level = new Parameters();
                level.Params = input.ReadObject<Dictionary<String, Object>>();

                return level;
            }
        }

    //[Serializable]
    public class Parameters
    {
        Dictionary<string, object> paramsDict = null;
        public Dictionary<string, object> Params { get { return paramsDict; } set { paramsDict = value; } }

        public Object this[String index]
        {
            set
            {
                if(paramsDict.ContainsKey(index))
                    Set(index, value);
                else
                    paramsDict.Add(index,value);
            }
            get
            {
                return Params[index];
            }
        }

        public Parameters()
        {
            paramsDict = new Dictionary<string, object>();
        }

        public Parameters(Dictionary<String, object> dict)
            : this()
        {
            this.paramsDict = dict;
        }

        public Parameters(Dictionary<String, String> dict)
            : this()
        {
            foreach (String key in dict.Keys)
                this.paramsDict[key] = (object)dict[key];
        }

        public Parameters(Parameters parms)
            : this()
        {
            this.paramsDict = new Dictionary<string, object>();
            foreach (String key in parms.paramsDict.Keys)
                paramsDict[key] = parms.paramsDict[key];
        }

        public Parameters(String key, object obj):this()
        {
            paramsDict.Add(key, obj);
        }

        public Parameters Merge(Parameters merge, Boolean overWrite)
        {
            Parameters merged = new Parameters(this.paramsDict);
            foreach (String key in merge.paramsDict.Keys)
            {
                merged[key] = merge[key];
            }
            return merged;
        }


        public Boolean ContainsKey(string key)
        {
            return paramsDict.ContainsKey(key);
        }

        public T GetValueAs<T>(String name, T defaultValue)
        {
            return GetValueAs<T>(name, defaultValue, true);
        }

        public T GetValueAs<T>(String name, T defaultValue, Boolean saveConversion)
        {
            //if it exists
            if (!paramsDict.ContainsKey(name))
                return defaultValue;

            //if its the correct strType
            if (paramsDict[name].GetType() == typeof(T))
                return (T)paramsDict[name];

            //convert
            //we have to special handle some common data types (vectors)
            if (typeof(T) == typeof(Vector3))
            {
                String[] splits = paramsDict[name].ToString().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                if (splits.Length != 3)
                {
                    Logging.Logger.Error("Parameters: failed to convert param " + name + " to vector3");
                    return defaultValue;
                }

                Vector3 c = new Vector3(float.Parse(splits[0]), float.Parse(splits[1]), float.Parse(splits[2]));
                if (saveConversion)
                    paramsDict[name] = (Object)c;
                
                return (T)((Object)c);
            }

            if (typeof(T) == typeof(Vector2))
            {
                String[] splits = paramsDict[name].ToString().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                if (splits.Length != 2)
                {
                    Logging.Logger.Error("Parameters: failed to convert param " + name + " to vector2");
                    return defaultValue;
                }
                Vector2 c = new Vector2(float.Parse(splits[0]), float.Parse(splits[1]));
                if (saveConversion)
                    paramsDict[name] = (Object)c;

                return (T)((Object)c);
            }


            //lets try some manual conversions, since i dont have TypeDescriptor to do automated conversion
            //on the 360. arg.
            Object o = Activator.CreateInstance(typeof(T));
            if(typeof(T)==typeof(float))
            {
                o = (Object)float.Parse((String)paramsDict[name]);
            }
            else if (typeof(T) == typeof(Int16))
            {
                o = (Object)Int16.Parse((String)paramsDict[name]);
            }
            else if (typeof(T) == typeof(Int32))
            {
                o = (Object)Int32.Parse((String)paramsDict[name]);
            }
            else if (typeof(T) == typeof(Boolean))
            {
                o = (Object)Boolean.Parse((String)paramsDict[name]);
            }
            else // dont know what to do with this strType
            {
#if XBOX360
                Logging.Logger.Error("Unable to create TypeDescriptor to convert param on 360 for " + name + " of " + typeof(T).ToString());
#else
                //Handle generic objects that can be converted from string
                TypeConverter tc = TypeDescriptor.GetConverter(typeof(T));
                o = tc.ConvertTo(paramsDict[name], typeof(T));
#endif
            }

           
            //save our conversion?
            if (saveConversion)
                paramsDict[name] = o;

            //return
            return (T)o;
        }

        //TODO: add, get, set, remove, clear, seralizable?
        public void Clear()
        {
            paramsDict.Clear();
        }

        public void Set(string key, object obj)
        {
            if (paramsDict.ContainsKey(key))
            {
                paramsDict[key] = obj;
            }
        }

        public void Add(string key, object obj)
        {
            paramsDict.Add(key, obj);
        }
    }
}